/**
 * @file config_by_device_type.c
 * @author Linquan  
 * @brief 对于不同类型的配置文件，进行文件解析处理。
 * @version 1.0
 * @date 2024-08-31
 * 
 * @copyright Copyright (c) 2024
 * history：
 * 2024-8-31：完成对 下接设备的配置文件的解析函数。后期可能还有 解析联动公式的配置文件。
 */
#include "../utils/macro_config.h"
#include "config_by_device_type.h"
#include "csvlib.h"

#include "../port/master_ctr_circuit_node.h"
#include "dev_table_by_config.h"

#define CONFIG_IDLE        0
#define CONFIG_SUCESS      1
#define CONFIG_FAIL        2

static uint8_t ConfigSucessFlag = CONFIG_IDLE;

/**> 存放 配置文件路径的前缀，一般是放在 可执行文件同级目录下的config文件夹中 */ 
char* ConfigFileDir = "src/config/";




/**> 90A 作为 本机设备，需要单独创建一块全局变量，
 * 
 * 其他的类型设备的配置文件解析，均在函数中创建局部变量解决 
 * 类似 91A、中继模块等，其节点数量计数器，在其配置信息中均已定义，可以通过节点树取得
 * */
dev_type_config_info_t format_90A = 
{
    .file_name = "src/config/90A_config.csv",
    .line_all_circuit_num = ALL_CIRCUIT_NUME_LINE_INDEX,
    .line_circuit_info_start = CITCUIT_INFO_LINE_INDEX, 
    .node_tree1 = MasterMangmentData.master_node_tree ,      /**> 把90A的节点树指针 指过来 */
    .node_add_callback = direct_node_table_add,
};



/**
 * @brief 判断 节点配置是否全部成功
 * 
 * @return uint8_t 
 */
uint8_t is_config_sucess()
{
    return ConfigSucessFlag;
}

/**
 * @brief 用于 设备配置文件解析的函数。
 * 适用设备： 90A、91A
 *  配置过程中，任何一个环节出现问题，立刻设置配置状态为失败，并退出。
 * @param format_info 
 * @return int ：
 *              前边的配置出现失败，就停止配置，返回-1。
 *              打开文件失败，返回 -1 ERROR 。
 *              正常退出，返回1 TRUE
 */
static int anaylyse_config_file(dev_type_config_info_t *format_info)
{
    // TODO： 判断 之前的配置标志是否存在失败，如果失败，则立刻返回，表示配置停止，需要检查配置文件。
    
    int ret = TRUE;
    int8_t  row[256] = {0};
    int8_t  *token;

    size_t table_circuit_num = 0;    /**> 配置文件中 所有回路的总数量 ，用于循环获得每个回路的节点信息*/ 
    size_t table_node_number = 0;    /**> 表头中所有回路的 节点总数,用于循环获得每行节点信息*/

    cfg_circuit_overview_t   table_head;          /**> 从配置表 获得一个回路的总览 */
    cfg_node_info_t        config_node_info;     /**> 一个节点的配置信息 */


    if(ConfigSucessFlag == CONFIG_FAIL)
    {
        error_log("the last config is fail ,stop config!\n");
        ret = ERROR;
       return ret;
    }

    debug_log( "anaylyse file name :%s \n", format_info->file_name );
    FILE *fp = fopen(format_info->file_name, "r");

    if(fp == NULL)
    {
        error_log("file open fail \n");
        ret = ERROR;
        return ret;
    }

    /**> 1、读配置文件: 所有回路总数 */
    
    CSV_get_a_row_data(fp, format_info->line_all_circuit_num, row, sizeof(row));
    debug_log("line: %s", row);

    CSV_anaylise_colum_data_from_row(row, get_circuit_number,&table_circuit_num);
    format_info->ciucuit_nume = table_circuit_num; /**> 保存配置文件中的 回路总数 */

    /**> 1.1、用回路总数创建一个临时变量，用于记录这个配置文件中每个回路设置了多少节点 */
    uint8_t arry[table_circuit_num] ;

    /**> 1.2、 创建一个临时数组，保存配置文件中每个回路中节点数量 */
    format_info->cfg_node_num_ptr = arry;     

    printf(" 回路总数：%ld \n",table_circuit_num);

    /**> 2、从90A配置文件中，读取表头，依次读取一个回路的ID和节点数量，并配置到节点树中 */
    for(int i = 0; i < table_circuit_num ; i++)
    {
        /**> 2.1 读取一行并解析。取出该回路ID和 回路中的节点数量 */
        CSV_get_a_row_data( fp,\
                            (i + format_info->line_circuit_info_start),\
                            row, sizeof(row));
        debug_log("line %d: %s", (i + format_info->line_circuit_info_start), row);

        CSV_anaylise_colum_data_from_row(row, creat_one_node_tree,&table_head);
       
        /**> 2.2、累加 配置文件中节点的总数 */
        table_node_number += table_head.circuit_node_num;
        debug_log(" circuit number is %ld\n",table_node_number);

        /**> 2.3、根据配置文件中的解析出的回路总览信息，在对应节点树分配空间 */
        create_circuit_node_by_table(format_info, table_head);
    }

    /**> 3、从90A配置文件中，读取一个节点的配置信息，并配置到具体的节点上 */
    for (int i = 0; i < table_node_number; i++)
    {
        CSV_get_a_row_data( fp,\
                            (i + table_circuit_num + NODE_INFO_FIX_LINE_INDEX),\
                            row, sizeof(row));

        //debug_log("line %ld: %s",  (i + table_circuit_num + NODE_INFO_FIX_LINE_INDEX), row);
        CSV_anaylise_colum_data_from_row(row, analyse_node_info, &config_node_info);

        /**> 配置节点时出现错误，立刻停止循环 */
        if (ERROR == trans_config_data_to_node(format_info, config_node_info))
        {
           break;
        }    
    }
    
    /**> 4、检查 配置结果是否匹配的函数，来判断是否配置成功。
     *  在多个文件调用该函数时，只要有一张配置文件配置失败，就判定失败。
    */
    if( check_config_node_num_is_equl(format_info))
    {
        ConfigSucessFlag = CONFIG_SUCESS;
    }
    else
    {
        ret = ERROR;
        ConfigSucessFlag = CONFIG_FAIL;
        error_log(" config file :%s. Configuration quantity and planned quantity are not equal ",\
                    format_info->file_name);    
    }

    fclose(fp);

    return ret;
}





/**
 * @brief 根据配置文件，配置中继模块信息
 * 
 * @param table_index 
 * @return int 
 */
int config_relay_mode_by_cfg_file(uint8_t table_index)
{
    int ret = 0;
     
    /* 1、从 非直接相连的表中取出一个节点*/
    circuit_node_t * node_ptr = node_table_read_one(NODE_INDIRECT, table_index);

    debug_log(" indirect node name %s \n",node_ptr->dev_description);

    /* 2、判断当前配置的节点算91A，并进行相关配置。*/
    if(node_ptr->node_type == TYPE_MOD_RELAY)
    {
        /**> 2.1、得到该节点中 的配置文件的文件名，并拼接上路径前缀 
            TODO:后期需要根据配置文件存放路径进行修改。最好是放在同级目录下，这样就不用加前缀路径了
            !!! 记得在 解析完该文件后 释放 file_name 空间
        */
        CONFIG_INFO_TYPE(TYPE_MOD_RELAY) *cfg_data_relay_mod = \
            (CONFIG_INFO_TYPE(TYPE_MOD_RELAY)*) node_ptr->node_config_data;

        char* file_name_from_node = \
        cfg_data_relay_mod->config_file_name;

        char* file_name = malloc(1 + strlen(ConfigFileDir) + strlen(file_name_from_node));
        strcpy(file_name, ConfigFileDir);
        strcat(file_name, file_name_from_node);           


        /* 2.4、 使用上述信息配置 解析信息结构体  */
        dev_type_config_info_t format_relay_mod =
        {               
            .file_name = file_name,
            .line_all_circuit_num = ALL_CIRCUIT_NUME_LINE_INDEX,
            .line_circuit_info_start = CITCUIT_INFO_LINE_INDEX, 
            .node_tree1 = &cfg_data_relay_mod->node_tree_ptr,   /**> 将需要配置的节点树取出送入配置文件 */     
            .node_add_callback = NULL,                          /**> 中继模块下接的节点不需要再配置了 */
        };

        /**> 2.5、传入解析函数 */
        ret = anaylyse_config_file(&format_relay_mod);
        if(ret == ERROR)
        {
            error_log("relay mod config is fail,current dev index is %d ", table_index);
        }

        /**> 解析结束后，不论成功与否，先把空间释放掉 */
        free(file_name);

        return ret;
    }
}


/**
 * @brief 根据配置文件配置 91A
 * 
 * @param table_index 
 * @return int 
 */
int config_91A_by_cfg_file(uint8_t table_index)
{
    int ret = 0;

     /* 1、从表中取出一个节点*/
    circuit_node_t * node_ptr = node_table_read_one(NODE_DIRECT, table_index);

    debug_log(" node name %s \n",node_ptr->dev_description);

    /* 2、判断当前配置的节点算91A，并进行相关配置。*/
    if(node_ptr->node_type == TYPE_CTR_CW1320_91A)
    {
        /**> 2.1、得到该节点中 的配置文件的文件名，并拼接上路径前缀 
            TODO:后期需要根据配置文件存放路径进行修改。最好是放在同级目录下，这样就不用加前缀路径了
            !!! 记得在 解析完该文件后 释放 file_name 空间
        */     
        CONFIG_INFO_TYPE(TYPE_CTR_CW1320_91A) *cfg_data_91A = \
            (CONFIG_INFO_TYPE(TYPE_CTR_CW1320_91A)*) node_ptr->node_config_data;
        
        char* file_name_from_node = \
        cfg_data_91A->config_file_name;
        
        char* file_name = malloc(1 + strlen(ConfigFileDir) + strlen(file_name_from_node));
        strcpy(file_name, ConfigFileDir);
        strcat(file_name, file_name_from_node);           

        /* 2.2、 使用上述信息配置 解析信息结构体  */
        dev_type_config_info_t format_91A =
        {               
            .file_name = file_name,
            .line_all_circuit_num = ALL_CIRCUIT_NUME_LINE_INDEX,
            .line_circuit_info_start = CITCUIT_INFO_LINE_INDEX, 
            .node_tree1 =  cfg_data_91A->mod_91a_node_tree_ptr,  /**> 将需要配置的节点树取出送入配置文件 */      
            .node_add_callback = indirect_node_table_add,       /**> 使用向非直接相连的表格中添加的函数 */
        };

        /**> 2.5、传入解析函数 */
        ret = anaylyse_config_file(&format_91A);

        /**> 解析结束后，不论成功与否，先把空间释放掉 */
        free(file_name);

        if(ret < 0)
        {
            error_log("91A config is fail,current dev index is %d ", table_index);
            return ret;
        }

        /**> TODO: 配置中继模块 */
        indirect_node_table_print();

        for (int i = 0; i < get_node_number(NODE_INDIRECT); i++)
        {
           ret = config_relay_mode_by_cfg_file(i);

           if(ret == ERROR) break;
        }
        
        /**> TODO: 清空 非直接相连的节点表格（表格里是 中继模块的配置节点） */
        node_table_clear(NODE_INDIRECT);

        return ret;
    }
}



/**
 * @brief API: 对外界调用该函数，完成配置文件初始化
 * 先配置 90A，然后配置91A，然后配置中继模块。目前是按照这个顺序进行。
 * 也就是90A只能接91A以及其他不会下挂其他节点的设备。类似中继，接不上。
 */
void init_node_tree_by_config_file()
{
    int ret = 0;

    /**> 1、配置90A */
    ret = anaylyse_config_file(&format_90A);

    if(ret < 0)
    {
        error_log("90A config is fail ");
        return;
    }
    
    

    /**> 2、配置91A*/
    for(int table_index = 0; table_index<  get_node_number(NODE_DIRECT); table_index++)
    {
        ret = config_91A_by_cfg_file(table_index);
       
        if (ret == ERROR)
        {
            break;
        }       
    }
}










